﻿// ***********************************************************************
// Assembly         : Delta_entidad
// Author           : Juan Alberto Cerino Gomez
// Created          : 02-18-2013
//
// Last Modified By : Juan Alberto Cerino Gomez
// Last Modified On : 06-02-2013
// ***********************************************************************
// <copyright file="TipoBase.cs" company="Delta Bussines">
//     Copyright (c) 2013  Delta Bussines. All rights reserved.
// </copyright>
// <summary></summary>
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Delta_util.Clases;
using System.Dynamic;
using System.Collections;
using System.Xml;
using System.Data.SqlTypes;
using System.ComponentModel;
using Delta_entidad.EntidadesControl;
using Delta_entidad.Atributos;

namespace Delta_entidad.Base
{
    /// <summary>
    /// Clase base que contiene todos los metodos y variables necesarios para el funcionamiento de las entidades dinamicas.
    /// </summary>
    [Serializable()]
    public class TipoBase : Base.ITipo
    {
        #region CAMPOS USERS

        /// <summary>
        /// Constante que indica el nombre de la propiedad que contiene la lista de registros.
        /// </summary>
        private const string nombreLista = "listadoGral";

        /// <summary>
        /// Variable que almacena los valores de los campos dinámicos de la entidad.
        /// </summary>
        private string _campos_user;

        /// <summary>
        /// Variable que almacena el valor que indica si la entidad es usada por el Delta Grid o es definida por el usuario.
        /// </summary>
        private bool _esEntidadControl = false;

        #region Control dinamico
        /// <summary>
        /// Evento que ocurre cuando el valor de una propiedad cambia.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// Variable que almacena la informacion dinámica de los campos.
        /// </summary>
        private Dictionary<string, Cat_campo_tecnico> varInfoCampo = new Dictionary<string, Cat_campo_tecnico>();
        /// <summary>
        /// Variable que almacena los valores de los campos dinámicos.
        /// </summary>
        private Dictionary<string, object> varDinamicas = new Dictionary<string, object>();

        /// <summary>
        /// Obtiene o estable la información dinámica de los campos.
        /// </summary>
        /// <value>Información dinámica de los campos.</value>
        public Dictionary<string, Cat_campo_tecnico> VarInfoCampo
        {
            get { return varInfoCampo; }
            set { varInfoCampo = value; }
        }
        /// <summary>
        /// Obtiene o establece los valores de los campos dinámicos.
        /// </summary>
        /// <value>Valores de los campos dinámicos.</value>
        public Dictionary<string, object> VarDinamicas
        {
            get { return varDinamicas; }
            set { varDinamicas = value; }

        }


        /// <summary>
        /// Obtiene o establece el valor que indica si la entidad es de control del Delta Grid, o es definida por el usuario.
        /// </summary>
        /// <value>Valor lógico, por default es false.</value>
        public bool EsEntidadControl
        {
            get { return _esEntidadControl; }
            set { _esEntidadControl = value; }
        }
        /// <summary>
        /// Metodo que se obtiene el valor de un campo dinámico.
        /// </summary>
        /// <param name="key">El nombre del campo dinámico.</param>
        /// <param name="value">EL valor que se obtiene del campo.</param>
        /// <returns>Indica si se logró obtener el valor del campo.</returns>
        public bool TryGetValue(string key, out object value)
        {


            return varDinamicas.TryGetValue(key, out value);


        }
        /// <summary>
        /// Metodo que se encarga de verificar si un campo existe en la propiedad.
        /// </summary>
        /// <param name="key">Nombre del campo a buscar.</param>
        /// <param name="incluirCampos">Indica si se debe incluir las propiedades de la entidad (true) o solo buscar en los campos dinamicos(false).</param>
        /// <returns>Indica si se encuentra el campo.</returns>
        public bool ExisteCampo(string key, bool incluirCampos = true)
        {
            bool existe = varDinamicas.Keys.Contains(key);
            if (!existe && incluirCampos)
                existe = Comun.ExistePropiedad(this, key);
            return existe;
        }
        /// <summary>
        /// Metodo que se encarga de asingar un valor a un campo dinámico.
        /// </summary>
        /// <param name="key">Nombre del campo dinámico.</param>
        /// <param name="value">Valor a asignar.</param>
        public void SetValue(string key, object value)
        {


            if (value is DateTime)
            {
                System.Globalization.CultureInfo infotemp = System.Threading.Thread.CurrentThread.CurrentCulture;
                try
                {
                    System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
                    value = ((DateTime)value).ToString(System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern);
                }
                catch { }
                finally
                {
                    System.Threading.Thread.CurrentThread.CurrentCulture = infotemp;
                }
            }
            varDinamicas[key] = value;

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(key));
            }
        }
        /// <summary>
        /// Metodo que se encarga de obtener los nombres de los campos dinámicos que contiene la entidad.
        /// </summary>
        /// <returns>IEnumerable{System.String}.</returns>
        public IEnumerable<string> GetKeys()
        {

            var keys = varDinamicas.Keys;

            return keys;
        }

        /// <summary>
        /// Metodo que se encarga de obtener los metadatos de un objeto, este metodo es interno
        /// </summary>
        /// <param name="parameter">Expresión a usar como parametro.</param>
        /// <returns>DynamicMetaObject.</returns>
        protected internal DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
        {

            return new ControlDinamico(parameter, this);
        }

        #endregion


        /// <summary>
        /// Obtiene o establece los valores de los campos dinámicos en formato XML.
        /// </summary>
        /// <value>Valores de los campos dinámicos.</value>
        public string campos_user
        {
            get
            {
                //Cada vez que se consulta este campo, se actualiza el XML de acuerdo al arreglo de keys y valores
                if (varDinamicas.Count > 0)
                {
                    StringBuilder xml_resultado = new StringBuilder();
                    xml_resultado.Append("<dat ");
                    foreach (string campo in varDinamicas.Keys)
                    {
                        if ((varInfoCampo[campo] == null || varInfoCampo[campo].es_dinamico) && !varInfoCampo[campo].es_virtual)
                        {
                            xml_resultado.Append(string.Format(" {0}=\"{1}\" ", campo, varDinamicas[campo]));
                        }
                    }
                    xml_resultado.Append(" />");
                    _campos_user = xml_resultado.ToString();
                }

                return _campos_user;

            }
            set { _campos_user = value; }
        }


        /// <summary>
        /// Se encarga de obtener los valores de los datos dinámicos en formato XML sin incuir una etiqueta ROOT.
        /// </summary>
        /// <returns>System.String.</returns>
        public string ToXMLSinRoot()
        {
            StringBuilder xml_resultado = new StringBuilder();
            xml_resultado.Append("<" + GetType().Name + " ");
            //--xml_resultado.Append("<" + GetType().Name + " >");
            foreach (PropertyInfo prop in GetType().GetProperties())
            {
                if (Comun.ObtenerAtributoEntidad<AttPropiedad>(GetType(), prop.Name) != null)
                {
                    if (prop.CanRead)
                    {
                        //--xml_resultado.Append(string.Format("<{0} xs:xt=\"string\">{1}</{0}>", prop.Name, prop.GetValue(this, null)));

                        xml_resultado.Append(string.Format(" {0}=\"{1}\"", prop.Name, prop.GetValue(this, null)));

                    }
                }
            }

            if (varDinamicas.Count > 0)
            {

                foreach (string campo in varDinamicas.Keys)
                {
                    //--xml_resultado.Append(string.Format("<{0} xs:xt=\"{2}\">{1}</{0}>", campo, varDinamicas[campo],varInfoCampo[campo].tipo.tipoXML));
                    xml_resultado.Append(string.Format(" {0}=\"{1}\"", campo, varDinamicas[campo]));
                }

            }
            xml_resultado.Append(" />");
            //--xml_resultado.Append(" </" + GetType().Name + ">");
            return xml_resultado.ToString();
        }
        /// <summary>
        /// Metodo que se encarga de fijar la configuración dinámica a la entidad.
        /// </summary>
        /// <param name="configuracion">Configuración dinámica.</param>
        public void FijarConfiguracionDinamica(Cat_tipo_gral configuracion)
        {
            //Hay que ver si es necesario guardar o no guardar la configuracion, por motivo de que la entidad se serializa
            if (configuracion == null)
                return;


            XmlDocument infoXML = null;
            if (!string.IsNullOrWhiteSpace(_campos_user))
            {
                infoXML = new XmlDocument();
                infoXML.LoadXml("<root>" + _campos_user + "</root>");
            }
            foreach (Cat_campo_tecnico campo in configuracion.campos)
            {
                if (campo.es_dinamico)
                {
                    varDinamicas[campo.nombre] = Comun.ObtenerValorXML(infoXML, campo.nombre, campo.tipo.tipo_codigo);
                    varInfoCampo[campo.nombre] = campo;
                }


            }

        }

        /// <summary>
        /// Metodo que se encarga de convertir un valor SqlXml a una cadena XML.
        /// </summary>
        /// <param name="fuente">Valor que se obtiene de la base de datos.</param>
        /// <param name="propiedadXML">Propiedad en la cual se coloca la cadena XLM, por default el nombre es "campos_user".</param>
        public void BD_a_XML(SqlXml fuente, string propiedadXML = "campos_user")
        {
            PropertyInfo propiedad = this.GetType().GetProperty(propiedadXML);
            if (propiedad != null && fuente != null)
            {
                propiedad.SetValue(this, fuente.Value, null);
            }

        }



        /// <summary>
        /// Metodo que se encarga de obtener el numero de fila de acuerdo a parametros de busqueda.
        /// </summary>
        /// <param name="configuracion">Configuración dinámica de la entidad.</param>        
        /// <param name="parametros">Permite asignar una lista de filtros para la lista</param>
        /// <returns>System.Int32.</returns>
        public int ObtenerFilaEdicion(Cat_tipo_gral configuracion, Dictionary<string, object> parametros)
        {
            List<ItemGral> resultado = new List<ItemGral>();
            XmlDocument ndoc = new XmlDocument();
            object datosTabla = Comun.ObtenerValorPropiedad(this, "dato");
            if (datosTabla != null && string.IsNullOrWhiteSpace(datosTabla.ToString()))
                return 0;

            ndoc.LoadXml(datosTabla.ToString());
            int fila = 1;
            foreach (XmlNode item in ndoc.SelectNodes("/tabla/dat"))
            {
                ItemGral nitem = new ItemGral();
                nitem.Fila = fila;
                nitem.id_tipo_gral = 0;
                nitem.id_tabla_gral = 0;
                nitem.campos_user = item.OuterXml;
                nitem.FijarConfiguracionDinamica(configuracion);
                if (ItemGralFiltro(nitem, parametros,true))
                    return fila;
                fila++;
            }
            return 0;
        }
        /// <summary>
        /// Metodo usado para generar las filas de una tabla XML, este metodo se usa en las entidades que son dinámicas.
        /// </summary>
        /// <param name="configuracion">Configuración dinámica de la entidad.</param>
        /// <param name="id_tabla">La clave de la tabla.</param>
        /// <param name="id_tipo_tabla">La clave del tipo de tabla.</param>
        /// <param name="datosFiltro">Permite asignar una lista de filtros para la lista</param>
        /// <returns>List{ItemGral}.</returns>
        public List<ItemGral> ObtenerITemsTabla(Cat_tipo_gral configuracion, int id_tabla, int id_tipo_tabla, Dictionary<string, object> datosFiltro = null)
        {

            List<ItemGral> resultado = new List<ItemGral>();
            XmlDocument ndoc = new XmlDocument();
            object datosTabla = Comun.ObtenerValorPropiedad(this, "dato");
            if (datosTabla != null && string.IsNullOrWhiteSpace(datosTabla.ToString()))
                return resultado;

            ndoc.LoadXml(datosTabla.ToString());
            int fila = 1;
            foreach (XmlNode item in ndoc.SelectNodes("/tabla/dat"))
            {
                ItemGral nitem = new ItemGral();
                nitem.Fila = fila;
                nitem.id_tipo_gral = id_tipo_tabla;
                nitem.id_tabla_gral = id_tabla;
                nitem.campos_user = item.OuterXml;
                nitem.FijarConfiguracionDinamica(configuracion);
                if (ItemGralFiltro(nitem, datosFiltro))
                    resultado.Add(nitem);
                fila++;
            }
            return resultado;

        }

        /// <summary>
        /// Metodo que se encarga de verificar el item general con la lista de filtros.
        /// </summary>
        /// <param name="item">Item a verificar.</param>
        /// <param name="datosFiltro">Configuración del filtro.</param>
        /// <param name="convertirTipoParametro">Indica si el valor de la columna se debe convertir al tipo de dato del parametro.</param>
        /// <returns>Valor que indica si el item cumple con los criterios de filtro.</returns>
        private bool ItemGralFiltro(ItemGral item, Dictionary<string, object> datosFiltro = null, bool convertirTipoParametro = false)
        {
            if (datosFiltro == null)
                return true;
            else
            {
                bool res = true;
                foreach (string clave in datosFiltro.Keys)
                {
                    object valor = item.ObtenerValor(clave);
                    if (convertirTipoParametro && valor != null)
                    {
                        valor = Comun.Convertir(valor, (datosFiltro[clave].GetType()));
                        if (!(valor != null && valor.Equals(datosFiltro[clave])))
                            return false;
                    }
                    else
                        if (!(valor != null && valor.Equals(datosFiltro[clave].ToString())))
                            return false;

                }
                return res;
            }

        }


        #endregion


        /// <summary>
        /// Metodo que se encarga de serializar en formato JSON la entidad.
        /// </summary>
        /// <returns>System.String.</returns>
        public string SerializarJSON()
        {
            return Utilerias.SerializarJSon(this);
        }


        /// <summary>
        /// Metodo que se encarga de asignar un valor a una propiedad de la entidad.
        /// </summary>
        /// <param name="propiedad">Nombre de la propiedad.</param>
        /// <param name="valor">Valor a asignar.</param>
        public void FijarValor(string propiedad, object valor)
        {

            PropertyInfo prop = this.GetType().GetProperty(propiedad);
            if (prop != null)
            {
                prop.SetValue(this, Comun.Convertir(valor, prop.PropertyType), null);
            }
            else
            {
                if (ExisteCampo(propiedad))
                    SetValue(propiedad, valor);
            }

        }

        /// <summary>
        /// Metodo que se encarga de obtener el valor de un campo de la entidad.
        /// </summary>
        /// <param name="propiedad">Indica el nombre del campo.</param>
        /// <param name="vDef">Valor por default que se devuelve si no se encuentra el campo.</param>
        /// <returns>System.Object.</returns>
        public object ObtenerValor(string propiedad, object vDef = null)
        {
            try
            {
                PropertyInfo prop = this.GetType().GetProperty(propiedad);
                if (prop != null)
                {
                    object val = prop.GetValue(this, null);
                    return val == null ? vDef : val;
                }
                else
                {
                    if (ExisteCampo(propiedad))
                    {
                        object valor = vDef;
                        TryGetValue(propiedad, out valor);
                        if (valor == null)
                            return vDef;
                        else
                            return valor;
                    }
                    else
                        return vDef;
                }



            }
            catch { return vDef; }
        }

        /// <summary>
        /// Metodo que se encarga de obtener un valor de una propiedad de la entidad.
        /// </summary>
        /// <typeparam name="T">Tipo de dato, el cual indica el tipo al que se convierte el dato.</typeparam>
        /// <param name="propiedad">Nombre de la propiedad.</param>
        /// <returns>Valor obtenido de la propiedad.</returns>
        public T ObtenerValor<T>(string propiedad)
        {
            try
            {
                if (ExisteCampo(propiedad, false))
                {
                    object valor;
                    TryGetValue(propiedad, out valor);
                    return Comun.Convertir<T>(valor);
                }
                else
                {

                    PropertyInfo prop = this.GetType().GetProperty(propiedad);
                    if (prop != null)
                    {
                        return (T)Convert.ChangeType(prop.GetValue(this, null), typeof(T));
                    }
                    else
                        return default(T);
                }
            }
            catch { return default(T); }
        }

    }
}
